﻿/* Copyright 2008-10 Jarrett Vance
* see http://www.codeplex.com/blogsvc/license */
namespace AtomSite.WebCore
{
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.Linq;
  using AtomSite.Domain;
  using AtomSite.WebCore.Properties;
  using StructureMap;

  public class AdminService
  {
    protected ILogService LogService;
    protected IAtomPubService AtomPubService;
    protected IAnnotateService AnnotateService;
    protected IAuthorizeService AuthorizeService;

    public AdminService(IAtomPubService atompub, IAnnotateService anno, IAuthorizeService auth,
      ILogService logger)
    {
      AtomPubService = atompub;
      AnnotateService = anno;
      AuthorizeService = auth;
      LogService = logger;
    }

    #region Themes

    public void ApplyTheme(Scope scope, string theme)
    {
      AppService appSvc = AtomPubService.GetService();

      if (scope.IsEntireSite)
      {
        //for top level, when set to default, set to null since it is default
        if (theme == "default") theme = null;
        appSvc.Theme = theme;
      }
      else if (scope.IsWorkspace) appSvc.GetWorkspace(scope.Workspace).Theme = theme;
      else if (scope.IsCollection) appSvc.GetCollection(scope.Workspace, scope.Collection).Theme = theme;

      AtomPubService.UpdateService(appSvc);
    }

    #endregion

    #region Plugins

    //TODO: move all this to Plugin Engine


    public IEnumerable<PluginState> GetPlugins()
    {
      AppService appSvc = AtomPubService.GetService();
      return appSvc.Plugins.OrderByDescending(p => p.Merit);
    }

    public Plugin GetPlugin(string fullNameOfType, IContainer container, string appPath)
    {
      AppService appSvc = AtomPubService.GetService();
      string type = appSvc.Plugins.Where(p => p.Type.StartsWith(fullNameOfType)).Single().Type;
      IPlugin plugin = (IPlugin)container.GetInstance(Type.GetType(type));
      return plugin.GetPluginEntry(container, appPath);
    }

    public PluginState LoadAndInstallPlugin(Stream plugStream, string filename, string contentType, string appPath)
    {
      //TODO: auth

      if (!(contentType == "application/zip" || contentType == "application/x-zip-compressed" || contentType == "application/octet-stream"))
        throw new InvalidContentTypeException(contentType);
      
      string zipPath = Path.Combine(Path.Combine(appPath, Settings.Default.PluginsDir), filename);
      LogService.Info("Loading plugin into {0}", zipPath);
      //copy to bin directory
      using (FileStream fs = File.OpenWrite(zipPath))
      //using (MemoryStream fs = new MemoryStream())
      {
        byte[] bytes = new byte[plugStream.Length];
        int length = plugStream.Read(bytes, 0, (int)plugStream.Length);
        fs.Write(bytes, 0, length);
      }

      LogService.Info("Trying to install plugin...");
      PluginState ps = PluginEngine.InstallFromZip(zipPath, appPath, false);
      LogService.Info("Plugin installation for {0} resulted in plugin status of {1}", ps.Type, ps.Status);
      RegisterPlugin(ps);
      return ps;
    }

    public void InstallPlugin(string pluginType, string appPath, IContainer container)
    {
      //TODO: auth
      LogService.Info("Installing plugin {0}", pluginType);
      PluginState ps = PluginEngine.InstallFromZip(pluginType, appPath);
      RegisterPlugin(pluginType, ps);
    }

    protected void RegisterPlugin(string oldType, PluginState pluginEntry)
    {
      IList<PluginState> plugins = AtomPubService.GetService().Plugins.ToList();

      LogService.Info("Registering plugin {0}", pluginEntry.Type);
      var existing = plugins.Where(p => p.Type == oldType).SingleOrDefault();
      if (existing != null) plugins[plugins.IndexOf(existing)] = pluginEntry;
      else plugins.Add(pluginEntry);
      AppService svc = AtomPubService.GetService();
      svc.Plugins = plugins;
      AtomPubService.UpdateService(svc);
    }

    protected void RegisterPlugin(PluginState pluginEntry)
    {
      RegisterPlugin(pluginEntry.Type, pluginEntry);
    }

    public void SetupPlugin(string pluginType, string appPath, IContainer container)
    {
      //TODO: auth
      IList<PluginState> plugins = AtomPubService.GetService().Plugins.ToList();
      PluginState pluginEntry = plugins.Where(p => p.Type == pluginType).Single();
      LogService.Info("Finding plugin {0}", pluginType);
      Type type = Type.GetType(pluginType);
      IPlugin plugin = (IPlugin)container.GetInstance(type);

      LogService.Info("Setting up plugin {0}", pluginType);
      pluginEntry = plugin.Setup(container, appPath);

      //update registry
      RegisterPlugin(pluginEntry);
    }

    public void UninstallPlugin(string pluginType, string appPath, IContainer container)
    {
      //TODO: auth
      IList<PluginState> plugins = AtomPubService.GetService().Plugins.ToList();
      PluginState pluginEntry = plugins.Where(p => p.Type == pluginType).Single();
      Type type = Type.GetType(pluginType);
      IPlugin plugin = (IPlugin)container.GetInstance(type);
      LogService.Info("Uninstalling plugin");
      pluginEntry = plugin.Uninstall(container, appPath);
      //update registry
      RegisterPlugin(pluginEntry);
    }

    public void SetPluginEnabled(string type, bool enabled)
    {
      //TODO: authorization
      AppService appSvc = AtomPubService.GetService();
      PluginState plugin = appSvc.Plugins.Where(p => p.Type == type).Single();
      plugin.Status = enabled ? PluginStatus.Enabled : PluginStatus.Disabled;
      AtomPubService.UpdateService(appSvc);
    }

    public int SetPluginMerit(string type, string change)
    {
      AppService appSvc = AtomPubService.GetService();
      PluginState plugin = appSvc.Plugins.Where(p => p.Type == type).Single();
      switch (change)
      {
        case "next":
          if (Merit.HasNext(plugin.Merit)) plugin.Merit = (int)Merit.GetNext(plugin.Merit);
          else throw new Exception("There is no next merit.");
          break;
        case "prev":
          if (Merit.HasPrev(plugin.Merit)) plugin.Merit = (int)Merit.GetPrev(plugin.Merit);
          else throw new Exception("There is no previous merit.");
          break;
        case "add":
          if (plugin.Merit != int.MaxValue) plugin.Merit = plugin.Merit+1;
          else throw new Exception("The merit is already at max value.");
          break;
        case "subtract":
          if (plugin.Merit != int.MinValue) plugin.Merit = plugin.Merit - 1;
          else throw new Exception("The merit is already at min value.");
          break;
        default:
          throw new BaseException("The merit change value of '{0}' is not recognized.", change);
      }
      AtomPubService.UpdateService(appSvc);
      return plugin.Merit;
    }

    #endregion Plugins


  }
}
